package com.nmgydy.util;

import com.alibaba.fastjson.JSON;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Map;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import org.apache.log4j.Logger;

public class HttpsClient {

    private static final boolean isDebug = ConfigUtil.getBooleanValue("log.debug");
    private static final Logger logger = Logger.getLogger(HttpsClient.class);
    private static final int OK = 200;
    private static final int ConnectionTimeout = 5000;
    private static final int ReadTimeout = 5000;
    private static final String DEFAULT_CHARSET = "UTF-8";
    private static final String _GET = "GET";
    private static final String _POST = "POST";

    public HttpsClient() {
    }


    public static HttpClientResponse post(String url, Map<String,String> params) throws RuntimeException {
        String jsonString = JSON.toJSONString(params);
        return httpsRequest(url, _POST, jsonString, false, null, null, null);
    }

    public static HttpClientResponse postForm(String url, Map<String,String> params) throws RuntimeException {
        StringBuffer sb = new StringBuffer();
        boolean isFirst = true;
        for(String key : params.keySet()){
            if(isFirst){
                isFirst = false;
            }else{
                sb.append('&');
            }
            sb.append(key);
            sb.append('=');
            sb.append(URLEncoder.encode(params.get(key)));
        }
        System.out.println(sb.toString());
        return httpsRequest(url, _POST, sb.toString(), false, null, null, null);
    }

    public static HttpClientResponse get(String url) throws RuntimeException {
        return httpsRequest(url, _GET, null, false, null, null, null);
    }

    public static HttpClientResponse get(String url,Map<String,String> params) throws RuntimeException {
        StringBuffer sb = new StringBuffer(url);
        boolean isFirst = true;
        for(String key : params.keySet()){
            if(isFirst){
                sb.append('?');
                isFirst = false;
            }else{
                sb.append('&');
            }
            sb.append(key);
            sb.append('=');
            sb.append(params.get(key));
        }
        return httpsRequest(sb.toString(), _GET, null, false, null, null, null);
    }

    /**
     * Post XML格式数据
     * @param url 提交地址
     * @param xml XML数据
     * @return 输出流对象
     */
    public static HttpClientResponse postXml(String url, String xml) throws RuntimeException {
        return httpsRequest(url, _POST, xml, false, null, null, null);
    }

    /**
     * Post XML格式数据
     * @param url 提交地址
     * @param xml XML数据
     * @param partnerId 商户ID
     * @param certPath 证书地址
     * @param certSecret 证书密钥
     * @return 输出流对象
     */
    public static HttpClientResponse postXml(String url, String xml, String partnerId, String certPath, String certSecret) throws RuntimeException {
        return httpsRequest(url, _POST, xml, true, partnerId, certPath, certSecret);
    }

    /**
     * 通过https协议请求url
     *
     * @param url 提交地址
     * @param method 提交方式
     * @param postData 提交数据
     * @return 响应流
     * @throws RuntimeException
     */
    private static HttpClientResponse httpsRequest(String url, String method, String postData, boolean needCert, String partnerId, String certPath, String certSecret)
        throws RuntimeException {
        HttpClientResponse res = null;
        OutputStream output;
        HttpsURLConnection https;
        try {
            //创建https请求连接
            https = getHttpsURLConnection(url);
            //判断https是否为空，如果为空返回null响应
            if (https != null) {
                //设置Header信息，包括https证书
                setHttpsHeader(https, method, needCert, partnerId, certPath, certSecret);
                //判断是否需要提交数据
                if (method.equals(_POST) && null != postData) {
                    //讲参数转换为字节提交
                    byte[] bytes = postData.getBytes(DEFAULT_CHARSET);
                    //设置头信息
                    https.setRequestProperty("Content-Length", Integer.toString(bytes.length));
                    //https.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    //开始连接
                    https.connect();
                    //获取返回信息
                    output = https.getOutputStream();
                    output.write(bytes);
                    output.flush();
                    output.close();
                } else {
                    //开始连接
                    https.connect();
                }
                //创建输出对象
                res = new HttpClientResponse(https);
                if(isDebug){
                    logger.info("Http "+method+" - url:"+url+",params:"+postData+",response:"+res.asString());
                }
                //获取响应代码
                if (res.getStatus() == OK) {
                    return res;
                }
            }
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (NoSuchProviderException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyStoreException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (CertificateException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (UnrecoverableKeyException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        }
        return res;
    }

    public static void main(String[] args) {
        String xml = "<xml><body><![CDATA[孟昭祥(内蒙古大学艺术学院[中专]器乐专业)]]></body>" +
            "<mch_create_ip><![CDATA[127.0.0.1]]></mch_create_ip>" +
            "<sub_openid><![CDATA[oRBJSuFGYSa4Br1vjoBHyeULqnnc]]></sub_openid>" +
            "<notify_url><![CDATA[http://47.92.138.202/studentms-mobile/weixinNotify?schoolNumber=15009]]></notify_url>" +
            "<time_start><![CDATA[20170510094228]]></time_start>" +
            "<nonce_str><![CDATA[6abde172afee45e19066c7e82ab8d520]]></nonce_str>" +
            "<out_trade_no><![CDATA[150092017051009422800001]]></out_trade_no>" +
            "<sign><![CDATA[165FBBA03C80F2CDF27A1D54F87DE946]]></sign>" +
            "<time_expire><![CDATA[20170510102728]]></time_expire>" +
            "<mch_id><![CDATA[7551000001]]></mch_id>" +
            "<sub_appid><![CDATA[wx0a5ef53254e9c825]]></sub_appid>" +
            "<attach><![CDATA[A123456789|1.00]]></attach>" +
            "<total_fee><![CDATA[10]]></total_fee>" +
            "<service><![CDATA[pay.weixin.jspay]]></service>" +
            "<is_raw><![CDATA[1]]></is_raw>" +
            "</xml>";
        HttpClientResponse response = HttpsClient.postXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xml);
        System.out.println(response.asString());
    }

    /**
     * 获取https请求连接
     *
     * @param url 连接地址
     * @return https连接对象
     * @throws IOException
     */
    private static HttpsURLConnection getHttpsURLConnection(String url) throws IOException {
        URL urlGet = new URL(url);
        //创建https请求
        HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlGet.openConnection();
        return httpsUrlConnection;
    }

    private static void setHttpsHeader(HttpsURLConnection httpsUrlConnection, String method, boolean needCert, String partnerId, String certPath, String certSecret)
        throws NoSuchAlgorithmException, KeyManagementException, NoSuchProviderException,
        IOException, KeyStoreException, CertificateException, UnrecoverableKeyException {
        //不需要维修证书，则使用默认证书
        if (!needCert) {
            //创建https请求证书
            TrustManager[] tm = {new MyX509TrustManager()};
            //创建证书上下文对象
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            //初始化证书信息
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            //设置ssl证书
            httpsUrlConnection.setSSLSocketFactory(ssf);
        } else {
            //指定读取证书格式为PKCS12
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            //读取本机存放的PKCS12证书文件
            FileInputStream instream = new FileInputStream(new File(certPath));
            try {
                //指定PKCS12的密码
                keyStore.load(instream, partnerId.toCharArray());
            } finally {
                instream.close();
            }
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(keyStore, certSecret.toCharArray());
            //创建管理jks密钥库的x509密钥管理器，用来管理密钥，需要key的密码
            SSLContext sslContext = SSLContext.getInstance("TLSv1");
            // 构造SSL环境，指定SSL版本为3.0，也可以使用TLSv1，但是SSLv3更加常用。
            sslContext.init(kmf.getKeyManagers(), null, null);
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();
            //设置ssl证书
            httpsUrlConnection.setSSLSocketFactory(ssf);
        }
        //设置header信息
        httpsUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
        //设置User-Agent信息
        httpsUrlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
        //设置可接受信息
        httpsUrlConnection.setDoOutput(true);
        //设置可输入信息
        httpsUrlConnection.setDoInput(true);
        //设置请求方式
        httpsUrlConnection.setRequestMethod(method);
        httpsUrlConnection.setConnectTimeout(ConnectionTimeout);
        httpsUrlConnection.setReadTimeout(ReadTimeout);
        //设置编码
        httpsUrlConnection.setRequestProperty("Charsert", "UTF-8");
    }

    /**
     * 上传文件
     *
     * @param url 上传地址
     * @param file 上传文件对象
     * @return 服务器上传响应结果
     */
    public static String uploadHttps(String url, File file) throws RuntimeException {
        HttpsURLConnection https = null;
        StringBuffer bufferRes = new StringBuffer();
        try {
            // 定义数据分隔线
            String BOUNDARY = "----WebKitFormBoundaryiDGnV9zdZA1eM1yL";
            //创建https请求连接
            https = getHttpsURLConnection(url);
            //设置header和ssl证书
            setHttpsHeader(https, _POST, false, null, null, null);
            //不缓存
            https.setUseCaches(false);
            //保持连接
            https.setRequestProperty("connection", "Keep-Alive");
            //设置文档类型
            https.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);

            //定义输出流
            OutputStream out = null;
            //定义输入流
            DataInputStream dataInputStream;
            try {
                out = new DataOutputStream(https.getOutputStream());
                byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 定义最后数据分隔线
                StringBuilder sb = new StringBuilder();
                sb.append("--");
                sb.append(BOUNDARY);
                sb.append("\r\n");
                sb.append("Content-Disposition: form-data;name=\"media\";filename=\"").append(file.getName()).append("\"\r\n");
                sb.append("Content-Type:application/octet-stream\r\n\r\n");
                byte[] data = sb.toString().getBytes();
                out.write(data);
                //读取文件流
                dataInputStream = new DataInputStream(new FileInputStream(file));
                int bytes;
                byte[] bufferOut = new byte[1024];
                while ((bytes = dataInputStream.read(bufferOut)) != -1) {
                    out.write(bufferOut, 0, bytes);
                }
                out.write("\r\n".getBytes()); //多个文件时，二个文件之间加入这个
                dataInputStream.close();
                out.write(end_data);
                out.flush();
            } finally {
                if (out != null) {
                    out.close();
                }
            }

            // 定义BufferedReader输入流来读取URL的响应
            InputStream ins = null;
            try {
                ins = https.getInputStream();
                BufferedReader read = new BufferedReader(new InputStreamReader(ins, "UTF-8"));
                String valueString;
                bufferRes = new StringBuffer();
                while ((valueString = read.readLine()) != null) {
                    bufferRes.append(valueString);
                }
            } finally {
                if (ins != null) {
                    ins.close();
                }
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (NoSuchProviderException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyStoreException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (CertificateException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (UnrecoverableKeyException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } finally {
            if (https != null) {
                // 关闭连接
                https.disconnect();
            }
        }
        return bufferRes.toString();
    }

    /**
     * 下载附件
     *
     * @param url 附件地址
     * @return 附件对象
     * @throws RuntimeException
     */
    public static Attachment downloadHttps(String url) throws RuntimeException {
        //定义下载附件对象
        Attachment attachment = null;
        HttpsURLConnection https;
        try {
            //创建https请求连接
            https = getHttpsURLConnection(url);
            //设置header和ssl证书
            setHttpsHeader(https, _POST, false, null, null, null);
            //不缓存
            https.setUseCaches(false);
            //保持连接
            https.setRequestProperty("connection", "Keep-Alive");

            //初始化返回附件对象
            attachment = new Attachment();
            //根据下载响应内容创建输出流
            if (https.getContentType().equalsIgnoreCase("text/plain")) {
                // 定义BufferedReader输入流来读取URL的响应
                InputStream in = https.getInputStream();
                BufferedReader read = new BufferedReader(new InputStreamReader(in, "UTF-8"));
                String valueString;
                StringBuilder bufferRes = new StringBuilder();
                while ((valueString = read.readLine()) != null) {
                    bufferRes.append(valueString);
                }
                in.close();
                attachment.setError(bufferRes.toString());
            } else {
                BufferedInputStream bis = new BufferedInputStream(https.getInputStream());
                String ds = https.getHeaderField("Content-disposition");
                String fullName = ds.substring(ds.indexOf("filename=\"") + 10, ds.length() - 1);
                String relName = fullName.substring(0, fullName.lastIndexOf("."));
                String suffix = fullName.substring(relName.length() + 1);

                attachment.setFullName(fullName);
                attachment.setFileName(relName);
                attachment.setSuffix(suffix);
                attachment.setContentLength(https.getHeaderField("Content-Length"));
                attachment.setContentType(https.getHeaderField("Content-Type"));

                attachment.setFileStream(bis);
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (NoSuchAlgorithmException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyManagementException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (NoSuchProviderException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (KeyStoreException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (CertificateException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } catch (UnrecoverableKeyException ex) {
            throw new RuntimeException(ex.getMessage(), ex);
        } finally {
        }
        return attachment;
    }
}
